package gUploadFile

import (
	"errors"
	"gitee.com/zjlsliupei/ghelp"
	"github.com/rs/xid"
	"github.com/tidwall/gjson"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"
)

type Local struct {
	// {"uploadDir":"uploads","groupPattern":"DATETIME::{2006};$CORPID$;DATETIME::{01-02}","maxSize":1048576,"allowExt":".jpg", "denyExt":".exe"}
	// uploadDir: 上传目录，必填，建设权限0766
	// groupPattern: 目录分组模式，必填，"DATETIME::{2006-01-02};$CORPID$"，多级目录以;分开。其中DATETIME为系统自动模式，2006-01-02表里日期格式。自定义变量以$$包果，如果在SaveUploadFiles的时候传入变量，会自动替换变量
	// maxSize: 允许上传最大文件，可选，单位byte
	// allowExt: 扩展名白名单，可选，多个扩展名用|分割
	// denyExt: 扩展名黑名单，可选，多个扩展名用|分割
	option gjson.Result
}

func NewLocal(option string) (Local, error) {
	_option := gjson.Parse(option)
	if !_option.Get("uploadDir").Exists() {
		return Local{}, errors.New("uploadDir is required")
	}
	if !_option.Get("groupPattern").Exists() {
		return Local{}, errors.New("groupPattern is required")
	}
	l := Local{
		option: _option,
	}
	var err error
	if !ghelp.IsDir(l.option.Get("uploadDir").String()) {
		err = os.MkdirAll(l.option.Get("uploadDir").String(), os.FileMode(0766))
	}
	return l, err
}

func (l Local) SaveUploadFiles(headers []*multipart.FileHeader, groupParam map[string]string) ([]File, error) {
	files := make([]File, 0)
	if len(headers) < 1 {
		return files, errors.New("have no files")
	}
	// 转化成file
	var fileMetas []FileMeta
	for _, f := range headers {
		fileMetas = append(fileMetas, FileMeta{
			Filename: f.Filename,
			Size:     f.Size,
			Ext:      strings.ToLower(filepath.Ext(f.Filename)),
		})
	}

	// 先检查文件合法性
	for _, v := range fileMetas {
		if err := doBeforeSaveFunc(v, l.option); err != nil {
			return files, err
		}
	}
	// 再执行上传
	for _, v := range headers {
		if f, err := l.saveUploadFile(v, groupParam); err != nil {
			return files, err
		} else {
			files = append(files, f)
		}
	}
	return files, nil
}

func (l Local) saveUploadFile(header *multipart.FileHeader, groupParam map[string]string) (File, error) {
	f, err := header.Open()
	if err != nil {
		return File{}, err
	}
	defer f.Close()
	filePath := l.createFilepath(header.Filename, groupParam)
	dir := filepath.Dir(filePath)
	if !ghelp.IsDir(dir) {
		err = os.MkdirAll(dir, os.FileMode(0766))
		if err != nil {
			return File{}, err
		}
	}
	dst, err := os.Create(filePath)
	if err != nil {
		return File{}, err
	}
	defer dst.Close()
	_, err = io.Copy(dst, f)
	return File{
		Filename:    filepath.Base(filePath),
		Filepath:    filePath,
		Size:        header.Size,
		Ext:         strings.ToLower(filepath.Ext(filePath)),
		RawFilename: header.Filename,
	}, err
}

func (l Local) UploadLocalFile(filename string, groupParam map[string]string) (File, error) {
	return l.CopyFile(filename, groupParam)
}

func (l Local) DownloadFile(src string, dst string) error {
	_, err := ghelp.CopyFile(src, dst)
	return err
}

func (l Local) CopyFile(src string, groupParam map[string]string) (File, error) {
	f, err := os.Open(src)
	if err != nil {
		return File{}, err
	}
	fileInfo, err := os.Stat(src)
	if err != nil {
		return File{}, err
	}
	defer f.Close()
	// 先检查文件合法性
	if er := doBeforeSaveFunc(FileMeta{
		Filename: src,
		Size:     fileInfo.Size(),
		Ext:      strings.ToLower(filepath.Ext(src)),
	}, l.option); er != nil {
		return File{}, er
	}
	filePath := l.createFilepath(src, groupParam)
	dir := filepath.Dir(filePath)
	if !ghelp.IsDir(dir) {
		err = os.MkdirAll(dir, os.FileMode(0766))
		if err != nil {
			return File{}, err
		}
	}
	dst, err := os.Create(filePath)
	if err != nil {
		return File{}, err
	}
	defer dst.Close()
	_, err = io.Copy(dst, f)
	return File{
		Filename:    filepath.Base(filePath),
		Filepath:    filePath,
		Size:        fileInfo.Size(),
		Ext:         strings.ToLower(filepath.Ext(filePath)),
		RawFilename: src,
	}, err
}

func (l Local) createFilepath(filename string, groupParam map[string]string) string {
	filePath := filepath.Join(
		l.option.Get("uploadDir").String(),
		generateByGroupPattern(l.option.Get("groupPattern").String(), groupParam),
		xid.New().String()+strings.ToLower(filepath.Ext(filename)),
	)
	return filePath
}
